package cn.fresh.xuanyuan.framework.pay.core.client.impl.toutiao;

import cn.fresh.xuanyuan.framework.common.util.object.ObjectUtils;
import cn.fresh.xuanyuan.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.fresh.xuanyuan.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.fresh.xuanyuan.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.fresh.xuanyuan.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.fresh.xuanyuan.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
import cn.fresh.xuanyuan.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
import cn.fresh.xuanyuan.framework.pay.core.client.impl.AbstractPayClient;
import cn.fresh.xuanyuan.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.douyin.openapi.client.Client;
import com.douyin.openapi.credential.models.Config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.util.*;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;

@Slf4j
public abstract class AbstractTouTiaoPayClient extends AbstractPayClient<TouTiaoPayClientConfig> {

    protected Client client;

    public AbstractTouTiaoPayClient(Long channelId, String channelCode, TouTiaoPayClientConfig config) {
        super(channelId, channelCode, config);
    }

    @Override
    protected void doInit() {


    }

    @Override
    protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Throwable {
        return null;
    }

    @Override
    protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable {
        if(StringUtils.isNotBlank(body)) {
            JSONObject json = JSONObject.parseObject(body);
            String type = json.getString("type");
            String signature = json.getString("msg_signature");
            JSONObject data = json.getJSONObject("msg");
            String timestamp = json.getString("timestamp");
            String nonce = json.getString("nonce");
            Map<String, Object> map = new HashMap<>();
            map.put("timestamp", timestamp);
            map.put("nonce", nonce);
            map.put("msg", json.getString("msg"));
            map.put("type", type);
            List<String> sortedString = Arrays.asList(config.getPayToken(), timestamp, nonce, json.getString("msg"));
            String sign = TouTiaoSign.callbackSign(sortedString);
            if("payment".equalsIgnoreCase(type) && sign.equals(signature)) {
                Integer amount = data.getIntValue("total_amount");
                return PayOrderRespDTO.of(PayOrderStatusRespEnum.SUCCESS.getStatus(), data.getString("channel_no"), data.getString("seller_uid"), parseTime(data.getLong("paid_at")),
                        data.getString("cp_orderno"), body, amount);
            }
        }
        return null;
    }

    @Override
    protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
        Map<String, Object> map = new HashMap<>();
        map.put("app_id", config.getAppId());
        map.put("out_order_no", outTradeNo);
        String sign = TouTiaoSign.requestSign(map, config.getSalt());
        map.put("sign", sign);
        String res = HttpUtil.post(config.getQueryOrderUrl(), JSON.toJSONString(map));
        if(StringUtils.isNotBlank(res)) {
            JSONObject json = JSONObject.parseObject(res);
            if(json.getIntValue("err_no") == 0 && json.getJSONObject("payment_info") != null) {
                JSONObject paymentInfo = json.getJSONObject("payment_info");
                Integer amount = paymentInfo.getIntValue("total_fee");
                String orderStatus = paymentInfo.getString("order_status");
                Integer status = parseStatus(orderStatus);
                Assert.notNull(status,  () -> {
                    throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", paymentInfo));
                });
                Long payTime = paymentInfo.getLong("pay_time");
                String channelNo = paymentInfo.getString("channel_no");
                String sellerUid = paymentInfo.getString("seller_uid");
                return PayOrderRespDTO.of(status, channelNo, sellerUid, parseTime(payTime), outTradeNo, paymentInfo, amount);
            }
        }
        return null;
    }

    private static Integer parseStatus(String tradeStatus) {
        return Objects.equals("PROCESSING", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
                : Objects.equals(tradeStatus, "SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
                : ObjectUtils.equalsAny(tradeStatus,"FAIL", "TIMEOUT") ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
    }

    @Override
    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
        Map<String, Object> map = new HashMap<>();
        map.put("app_id", config.getAppId());
        map.put("out_order_no", reqDTO.getOutTradeNo());
        map.put("out_refund_no", reqDTO.getOutRefundNo());
        map.put("reason", reqDTO.getReason());
        map.put("refund_amount", reqDTO.getRefundPrice());
        map.put("notify_url", reqDTO.getNotifyUrl());
        String sign = TouTiaoSign.requestSign(map, config.getSalt());
        map.put("sign", sign);
        String res = HttpUtil.post(config.getRefundApiUrl(), JSON.toJSONString(map));
        log.info("doUnifiedRefund res: {}", res);
        if(StringUtils.isNotBlank(res)) {
            JSONObject json = JSON.parseObject(res);
            if(json.getIntValue("err_no") == 0 && "success".equals(json.getString("err_tips"))) {
                return PayRefundRespDTO.waitingOf(json.getString("refund_no"),
                        reqDTO.getOutRefundNo(), json);
            }
            else {
                return PayRefundRespDTO.failureOf(json.getString("err_no"), json.getString("err_tips"), reqDTO.getOutRefundNo(), res);
            }
        }
        return PayRefundRespDTO.failureOf("500", "请求响应为空", reqDTO.getOutRefundNo(), res);
    }

    @Override
    protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) throws Throwable {
        if(StringUtils.isNotBlank(body)) {
            JSONObject json = JSONObject.parseObject(body);
            String type = json.getString("type");
            String signature = json.getString("msg_signature");
            JSONObject data = json.getJSONObject("msg");
            String timestamp = json.getString("timestamp");
            String nonce = json.getString("nonce");
            Map<String, Object> map = new HashMap<>();
            map.put("timestamp", timestamp);
            map.put("nonce", nonce);
            map.put("msg", json.getString("msg"));
            map.put("type", type);
            List<String> sortedString = Arrays.asList(config.getPayToken(), timestamp, nonce, json.getString("msg"));
            String sign = TouTiaoSign.callbackSign(sortedString);
            if("refund".equalsIgnoreCase(type) && sign.equals(signature)) {
                if("success".equalsIgnoreCase(data.getString("status"))) {
                    return PayRefundRespDTO.successOf(data.getString("refund_no"), parseTime(data.getLong("refunded_at")),
                            data.getString("cp_refundno"), body);
                }
                else {
                    return PayRefundRespDTO.failureOf(data.getString("status"), data.getString("message"),data.getString("cp_refundno"), body);
                }
            }
        }
        return PayRefundRespDTO.failureOf(params.get("outRefundNo"), body);
    }

    @Override
    protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws Throwable {
        Map<String, Object> map = new HashMap<>();
        map.put("app_id", config.getAppId());
        map.put("out_refund_no", outRefundNo);
        String sign = TouTiaoSign.requestSign(map, config.getSalt());
        map.put("sign", sign);
        String res = HttpUtil.post(config.getQueryRefundOrderUrl(), JSON.toJSONString(map));
        if(StringUtils.isNotBlank(res)) {
            JSONObject json = JSON.parseObject(res);
            String code = json.getString("err_no");
            String msg = json.getString("err_tips");
            if(json.getIntValue("err_no") == 0) {
                JSONObject data = json.getJSONObject("refundInfo");
                String status = data.getString("refund_status");
                String refundNo = data.getString("refund_no");
                if("success".equalsIgnoreCase(status)) {
                    return PayRefundRespDTO.successOf(refundNo, parseTime(data.getLong("refunded_at")), outRefundNo, res);
                }
                else {
                    msg = data.getString("msg");
                }
            }
            return PayRefundRespDTO.failureOf(code, msg, outRefundNo, res);
        }
        return PayRefundRespDTO.failureOf(outRefundNo, res);
    }

    @Override
    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws Throwable {
        return null;
    }

    protected LocalDateTime parseTime(long mills) {
        try {
            return LocalDateTimeUtil.of(mills);
        }
        catch (Exception e) {

        }
        return LocalDateTimeUtil.now();
    }

}
